/*
 *  Copyright (C) 2011 glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.common.defaults;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.balisunrise.annotation.Menu;
import org.balisunrise.annotation.util.ReflectUtilities;
import org.balisunrise.application.Application;
import org.balisunrise.common.MenuAction;
import org.balisunrise.application.MenuFolder;
import org.balisunrise.application.MenusManager;
import org.balisunrise.application.Module;

/**
 * Classe que manté, os menus da Aplicação.
 *
 * @author glauco
 */
public class DefaultMenusManager implements MenusManager{

    List<Class> classes;

    List instances;

    Map<String,List<MenuAction>> actions;

    public DefaultMenusManager() {
        classes = new ArrayList<Class>();
        instances = new ArrayList();
        actions = new HashMap<String, List<MenuAction>>();
    }

    /**
     * Adiciona uma classe de ações de menu.
     * 
     * @param menuClass classe com as ações de menu.
     */
    @Override
    public void addMenuClass(Class menuClass) {

        if(menuClass == null)
            return ;

        if(classes.contains(menuClass))
            return ;

        if(!menuClass.isAnnotationPresent(Menu.class))
            return ;

        classes.add(menuClass);
        
        processNewMenu(menuClass);
    }

    /**
     * Pega as ações de menu de um módulo.
     * @param module Nome do módulo.
     * @return as ações do Módulo.
     */
    @Override
    public MenuAction[] getMenuActions(String module) {

        List<MenuAction> list = actions.get(module);

        if (list == null)
            return new MenuAction[0];

        return list.toArray(new MenuAction[list.size()]);
    }

    /**
     * Cria ações de menu para a classe.
     * É criado uma instancia para a classe, e lido os métodos com a anotação
     * {@link org.balisunrise.annotation.MenuAction} e criado ações de menu
     * {@link org.balisunrise.application.MenuAction}
     * @param menuClass
     */
    private void processNewMenu(Class menuClass) {

        ReflectUtilities r = new ReflectUtilities(menuClass);

        Object instance;

        try{
            instance = menuClass.newInstance();
            instances.add(instance);
        }catch(Exception ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "Classe de Menu não pode ser instânciada: "
                    + menuClass.getName(), ex);
            return ;
        }

        for (Method method : r.getDeclaredMethods()) {
            if(method.isAnnotationPresent(
                    org.balisunrise.annotation.MenuAction.class)){

                try{
                    org.balisunrise.annotation.MenuAction ma =
                            method.getAnnotation(
                            org.balisunrise.annotation.MenuAction.class);

                    DirectAction action =
                            new DirectAction(instance, method, false);

                    DefaultMenuAction menuAction = new DefaultMenuAction();

                    menuAction.setDescription(ma.text());
                    menuAction.setAction(action);
                    menuAction.setFolder(ma.folder());
                    menuAction.setIcon(ma.icon());
                    menuAction.setModule(ma.module());
                    menuAction.setName(ma.name());
                    menuAction.setToolTipText(ma.toolTipText());

                    addAction(menuAction);

                }catch(Exception ex){
                    Application.get().getLogger().log(Level.SEVERE,
                            "Falha ao construi MenuAction:"
                            + menuClass.getName() + "#"
                            + method.getName(), ex);
                }
            }
        }
    }

    /**
     * Adiciona a ação de menu a lista do mapa, usando o nome do módulo como
     * chave do mapa.
     *
     * @param action ação de menu.
     */
    private void addAction(MenuAction action) {

        List<MenuAction> list;

        if(actions.containsKey(action.getModule()))
            list = actions.get(action.getModule());
        else{
            list = new ArrayList<MenuAction>();
            actions.put(action.getModule(), list);
        }

        list.add(action);
    }

    @Override
    public void setModuleMenuActions(Module module) {
        
        List<MenuAction> list = actions.get(module.getName());

        if (list == null)
            return ;

        for (MenuAction ma : list) {
            String folderName = ma.getFolder();
            folderName = folderName.replace('\\', '/');
            
            String[] folders;
            
            if(folderName.isEmpty())
                folders = new String[0];
            else
                if(!folderName.contains("/"))
                    folders = new String[]{folderName};
                else
                    folders = folderName.split("/");

            addActionToFolder(folders, 0, module, ma);
        }
    }

    /**
     * método recurcivo.
     * @param folderNames array com o nome dos folder
     * @param i indece do array
     * @param folder folder atual
     * @param action ação de menu que será adicionado ao ultimo folder
     */
    private void addActionToFolder(String[] folderNames, int i,
            MenuFolder folder, MenuAction action){

        if(folderNames.length == i){
            folder.addAction(action);
            return ;
        }

        String folderName = folderNames[i];

        if(folder.containsFolder(folderName))
            folder = folder.getSubFolder(folderName);
        else
            folder = folder.createFolder(folderName);

        addActionToFolder(folderNames, i + 1, folder, action);
    }
}
